#pragma once

#include <pthread.h>
#include <queue>

const int g_cap = 5;

template<class T>
class blockqueue
{
public:
    blockqueue(int cap = g_cap)
        : _cap(cap)
    {
        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&productor, nullptr);
        pthread_cond_init(&consumer, nullptr);
    }

    bool isFull(){ return q.size() == _cap; }
    bool isEmpty(){ return q.empty(); }

    void push(const T& in)
    {
        pthread_mutex_lock(&mutex);
        while(isFull())
        {
            pthread_cond_wait(&productor, &mutex);
            // 当线程醒来之后是继续从wait函数开始向后运行
            // 所以可能存在被误唤醒的情况
            // 因此需要使用循环判断
        }

        q.push(in);

        pthread_cond_signal(&consumer);
        pthread_mutex_unlock(&mutex);
    }

    void pop(T* out)
    {
        pthread_mutex_lock(&mutex);
        while(isEmpty())
        {
            pthread_cond_wait(&consumer, &mutex);
        }

        *out = q.front();
        q.pop();

        pthread_cond_signal(&productor);
        pthread_mutex_unlock(&mutex);
    }

    ~blockqueue()
    {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&productor);
        pthread_cond_destroy(&consumer);
    }

private:
    std::queue<int> q;
    int _cap;
    pthread_mutex_t mutex;
    pthread_cond_t productor; // 控制生产者的条件变量
    pthread_cond_t consumer;  // 控制消费者的条件变量
};